#include "napi/native_api.h"
#include "hilog/log.h"
#include <cstdlib>
#include <future>

///////////////////////////////////////////safe thread实现异步转同步//////////////////////////////////////////////////
struct CallbackData {
    napi_threadsafe_function tsfn;
    napi_async_work work;
    char* url;
};

static void ExecuteWork(napi_env env, void *data)
{
    CallbackData *callbackData = reinterpret_cast<CallbackData *>(data);
    std::promise<std::string> promise;
    napi_call_threadsafe_function(callbackData->tsfn, &promise, napi_tsfn_nonblocking);
    auto future = promise.get_future();
    try {
        auto result = future.get();
        OH_LOG_Print(LOG_APP,LOG_INFO, 0xFF00, "testTag-Native",  "napi_call_threadsafe_function, Result from JS:\n %{public}s", result.c_str());
    } catch (const std::exception &e) {
        // OH_LOG_INFO(LOG_APP, "XXX, Result from JS %{public}s", e.what());
    }
}

static void WorkComplete(napi_env env, napi_status status, void *data)
{
    CallbackData *callbackData = reinterpret_cast<CallbackData *>(data);
    napi_release_threadsafe_function(callbackData->tsfn, napi_tsfn_release);
    napi_delete_async_work(env, callbackData->work);
    callbackData->tsfn = nullptr;
    callbackData->work = nullptr;
    delete[] callbackData->url;
}

static napi_value ResolvedCallback(napi_env env, napi_callback_info info)
{
    void *data = nullptr;
    size_t argc = 1;
    napi_value argv[1];
    if (napi_get_cb_info(env, info, &argc, argv, nullptr, &data) != napi_ok) {
        return nullptr;
    }
    size_t result = 0;
    // 获取字符串的长度
    napi_get_value_string_utf8(env, argv[0], NULL, 0, &result);
    // 分配足够的空间来存储字符串
    char *buf = new char[result + 1]; 
    napi_get_value_string_utf8(env, argv[0], buf, result + 1, &result);
    reinterpret_cast<std::promise<std::string> *>(data)->set_value(std::string(buf));
    return nullptr;
}

static napi_value RejectedCallback(napi_env env, napi_callback_info info)
{
    void *data = nullptr;
    if (napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &data) != napi_ok) {
        return nullptr;
    }
    reinterpret_cast<std::promise<std::string> *>(data)->set_exception(
        std::make_exception_ptr(std::runtime_error("Error in jsCallback")));
    return nullptr;
}

static napi_value ErrorCallback(napi_env env, napi_callback_info info)
{
    void *data = nullptr;
    size_t argc = 1;
    napi_value argv[1];
    if (napi_get_cb_info(env, info, &argc, argv, nullptr, &data) != napi_ok) {
        return nullptr;
    }
    size_t err = 0;
    // 获取字符串的长度
    napi_get_value_string_utf8(env, argv[0], NULL, 0, &err);
    // 分配足够的空间来存储字符串
    char *bufErr = new char[err + 1]; 
    napi_get_value_string_utf8(env, argv[0], bufErr, err + 1, &err);
    OH_LOG_Print(LOG_APP,LOG_INFO, 0xFF00, "testTag-Native",  "napi_call_threadsafe_function, err from JS:\n %{public}s", bufErr);
    return nullptr;
}

static void CallJs(napi_env env, napi_value jsCb, void *context, void *data)
{
    OH_LOG_Print(LOG_APP,LOG_INFO, 0xFF00, "testTag-Native",  "XXX, CallJs called");
    if (env == nullptr) {
        return;    
    }
    napi_value undefined = nullptr;
    napi_get_undefined(env, &undefined);
    // 创建一个string，作为ArkTS的入参 
    napi_value argv_in = nullptr; 
    CallbackData *callbackData = reinterpret_cast<CallbackData *>(context);
    OH_LOG_Print(LOG_APP,LOG_INFO, 0xFF00, "CallJs",  "callbackData url:\n %{public}s", callbackData->url);
    const char* url = "http://v1.yiketianqi.com/api?unescape=1&version=v91";
    //napi_create_string_utf8(env, url, strlen(url), &argv_in);
    napi_create_string_utf8(env, callbackData->url, strlen(callbackData->url), &argv_in);
    napi_value promise = nullptr;
    napi_call_function(env, undefined, jsCb, 1, &argv_in, &promise);
    napi_value thenFunc = nullptr;
    if (napi_get_named_property(env, promise, "then", &thenFunc) != napi_ok) {
        return;
    }
    napi_value resolvedCallback;
    napi_value rejectedCallback;
    napi_create_function(env, "resolvedCallback", NAPI_AUTO_LENGTH, ResolvedCallback, data,
                         &resolvedCallback);
    napi_create_function(env, "rejectedCallback", NAPI_AUTO_LENGTH, RejectedCallback, data,
                         &rejectedCallback);
    napi_value argv[2] = {resolvedCallback, rejectedCallback};
    napi_call_function(env, promise, thenFunc, 2, argv, nullptr);
    
    napi_value catchFunc = nullptr;
    if (napi_get_named_property(env, promise, "catch", &catchFunc) != napi_ok) {
        return;
    }
    napi_value errCallback;
    napi_create_function(env, "errorCallback", NAPI_AUTO_LENGTH, ErrorCallback, data,
                         &errCallback);
    napi_value argc[1] = {errCallback};
    napi_call_function(env, promise, catchFunc, 2, argc, nullptr);
}

static napi_value doJobWithAsyncTask(napi_env env, napi_callback_info info)
{
    OH_LOG_Print(LOG_APP,LOG_INFO, 0xFF00, "testTag-Native",  "XXX, in doJobWithAsyncTask");
    size_t argc = 1;
    napi_value args[1] = {nullptr}; 
    // 获取传入的参数并依次放入参数数组中 
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    // 获取module 
    napi_value tsModule; 
    napi_load_module_with_info(env, "clibrary/src/main/ets/pages/TestModule","com.example.selectcontact/clibrary", &tsModule); 
    // 获取对象的方法 
    napi_value testLoadModule; 
    napi_get_named_property(env, tsModule, "httpCallback", &testLoadModule);
//     napi_get_named_property(env, tsModule, "callback", &testLoadModule);
    napi_value resourceName;
    napi_create_string_utf8(env, "testLoadModule_callback", NAPI_AUTO_LENGTH, &resourceName);
    //init CallbackData
    auto callbackData = new CallbackData();
    const char* initialUrl = "http://v1.yiketianqi.com/api?unescape=1&version=v91";
    callbackData->url = new char[strlen(initialUrl) + 1];
    strcpy(callbackData->url, initialUrl);
    napi_create_threadsafe_function(env, testLoadModule, nullptr,resourceName, 0, 1, callbackData, nullptr,callbackData, CallJs, &callbackData->tsfn);
    OH_LOG_Print(LOG_APP,LOG_INFO, 0xFF00, "testTag-Native",  "XXX, napi_create_threadsafe_function called");
    /*napi_value jsCb = nullptr;
    CallbackData *callbackData = nullptr;
    napi_get_cb_info(env, info, &argc, &jsCb, nullptr, reinterpret_cast<void **>(&callbackData));

    // 创建一个线程安全函数
    napi_value resourceName = nullptr;
    napi_create_string_utf8(env, "Thread-safe Function Demo", NAPI_AUTO_LENGTH, &resourceName);
    napi_create_threadsafe_function(env, jsCb, nullptr, resourceName, 0, 1, callbackData, nullptr, 
        callbackData, CallJs, &callbackData->tsfn);*/

    // 创建一个异步任务
    napi_create_async_work(env, nullptr, resourceName, ExecuteWork, WorkComplete, callbackData,
        &callbackData->work);
    OH_LOG_Print(LOG_APP,LOG_INFO, 0xFF00, "testTag-Native",  "XXX, napi_create_async_work called");
    // 将异步任务加入到异步队列中
    napi_queue_async_work(env, callbackData->work);
    OH_LOG_Print(LOG_APP,LOG_INFO, 0xFF00, "testTag-Native",  "XXX, napi_queue_async_work called");
    return nullptr;
}

///////////////////////////////////////////safe thread实现异步转同步//////////////////////////////////////////////////


///////////////////////////////////////////实现promise返回回调//////////////////////////////////////////////////
// 定义Promise对象then属性的回调方法 
// then属性的回调方法可以无返回值 
// 后文需要通过napi_create_function创建ArkTS函数对象，所以设置返回值为napi_value，并在函数末尾返回nullptr 
napi_value ThenCallBack(napi_env env, napi_callback_info info) { 
    size_t argc = 1; 
    napi_value args[1] = {nullptr}; 
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 
    size_t length;
    // 获取字符串的长度
    napi_get_value_string_utf8(env, args[0], NULL, 0, &length);
    // 分配足够的空间来存储字符串
    char *str = new char[length + 1]; 
    // 获取字符串内容
    napi_get_value_string_utf8(env, args[0], str, length + 1, &length); 
    OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "testTag-Native", "ArkTS Async Method Calculation Success, Result: %{public}s", 
                 str); 
    return nullptr; 
}

// 定义Promise对象catch属性的回调方法 
// catch属性的回调方法可以无返回值 
// 后文需要通过napi_create_function创建ArkTS函数对象，所以设置返回值为napi_value，并在函数末尾返回nullptr 
napi_value CatchCallBack(napi_env env, napi_callback_info info) { 
    size_t argc = 1; 
    napi_value args[1] = {nullptr}; 
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 
    size_t strLen = 0; 
    napi_get_value_string_utf8(env, args[0], nullptr, 0, &strLen);            // 获取字符串长度到strLen 
    char *strBuffer = new char[strLen + 1];                                   // 分配合适大小的char数组 
    napi_get_value_string_utf8(env, args[0], strBuffer, strLen + 1, &strLen); // 获取字符串，表示ArkTS侧异步方法计算异常的信息 
    OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "testTag-Native", 
                 "ArkTS Async Method Calculation Exception: %{public}s", strBuffer); 
    return nullptr; 
}

static napi_value TestPromise(napi_env env, napi_callback_info info) { 
    size_t argc = 1; 
    napi_value args[1] = {nullptr}; 
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); // 解析ArkTS侧传入的回调callback 
 
    napi_value arktsPromise = nullptr; 
    // 通过napi_call_function执行callback，返回ArkTS侧创建的promise对象 
    napi_call_function(env, nullptr, args[0], 0, nullptr, &arktsPromise); 
 
    // 获取promise对象的then属性，该属性的回调方法用于处理ArkTS侧异步计算结果 
    napi_value thenProperty = nullptr; 
    napi_get_named_property(env, arktsPromise, "then", &thenProperty); 
    // 将C++语言定义的then属性回调方法转换为ArkTS函数对象，即napi_value类型值 
    napi_value thenCallback = nullptr; 
    napi_create_function(env, "thenCallback", NAPI_AUTO_LENGTH, ThenCallBack, nullptr, &thenCallback); 
 
    // 获取promise对象的catch属性，该属性的回调方法用于处理ArkTS侧异步计算异常的信息 
    napi_value catchProperty = nullptr; 
    napi_get_named_property(env, arktsPromise, "catch", &catchProperty); 
    // 将C++语言定义的catch属性回调方法转换为ArkTS函数对象，即napi_value类型值 
    napi_value catchCallback = nullptr; 
    napi_create_function(env, "catchCallback", NAPI_AUTO_LENGTH, CatchCallBack, nullptr, &catchCallback); 
     
    // 通过napi_call_function执行then属性的回调，类似于ArkTS侧调用promise.then(()=>{}) 
    napi_call_function(env, arktsPromise, thenProperty, 1, &thenCallback, nullptr); 
    // 通过napi_call_function执行catch属性的回调，类似于ArkTS侧调用promise.catch(()=>{}) 
    napi_call_function(env, arktsPromise, catchProperty, 1, &catchCallback, nullptr); 
    return nullptr; 
}
///////////////////////////////////////////实现promise返回回调//////////////////////////////////////////////////

static napi_value Add(napi_env env, napi_callback_info info)
{
    size_t argc = 2;
    napi_value args[2] = {nullptr};
    //解析入参，入参信息全部封装在了napi_callback_info中
    napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);

    //根据实际情况取参数
    int value0;
    napi_get_value_int32(env, args[0], &value0);
    //根据实际情况取参数
    double value1;
    napi_get_value_double(env, args[1], &value1);

    double ret = value0 + value1;
    //将native的运算结果转为可对外输出类型napi_value
    napi_value sum;
    napi_create_double(env, ret, &sum);

    return sum;
}

static napi_value NAPI_Global_nativeCallArkTS(napi_env env, napi_callback_info info) {
    size_t argc = 1; 
    // 声明参数数组 
    napi_value args[1] = {nullptr}; 

    // 获取传入的参数并依次放入参数数组中 
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 
    // 创建一个string，作为ArkTS的入参 
    napi_value argv = nullptr; 
    napi_create_string_utf8(env, "value from native", 17, &argv); 
    
    // 调用传入的callback，并将其结果返回 
    napi_value result = nullptr; 
    napi_call_function(env, nullptr, args[0], 1, &argv, &result); 
    
    return result;
}

static napi_value NAPI_Global_nativeCallArkTSByObj(napi_env env, napi_callback_info info) {
    size_t argc = 2; 
    // 声明参数数组 
    napi_value args[2] = {nullptr}; 

    // 获取传入的参数并依次放入参数数组中 
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 
    
    // 获取对象的方法 
    napi_value process; 
    napi_get_named_property(env, args[0], "process", &process); 
    
    // 创建一个string，作为ArkTS的入参 
    napi_value argv = nullptr; 
    napi_create_string_utf8(env, "value from native", 17, &argv); 
    
    // 调用传入的对象的某个接口，并将其结果返回 
    napi_value result = nullptr; 
    napi_call_function(env, nullptr, process, 1, &argv, &result); 
    return result;
}

static napi_value NAPI_Global_NativeCallArkTSByLoadModule(napi_env env, napi_callback_info info) {
    size_t argc = 1; 
    // 声明参数数组 
    napi_value args[1] = {nullptr}; 
    // 获取传入的参数并依次放入参数数组中 
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 
    // 获取module 
    napi_value tsModule; 
    napi_load_module_with_info(env, "clibrary/src/main/ets/pages/TestModule","com.example.selectcontact/clibrary", &tsModule); 
    
    // 获取对象的方法 
    napi_value testLoadModule; 
    napi_get_named_property(env, tsModule, "testLoadModule", &testLoadModule); 
    
    // 获取对象的方法 
    napi_value value; 
    napi_get_named_property(env, tsModule, "value", &value);
    
    // 创建一个string，作为ArkTS的入参 
    napi_value argv = nullptr; 
    napi_create_string_utf8(env, "value from native", 17, &argv);
    
    // 调用传入的callback，并将其结果返回 
    napi_value result = nullptr; 
    napi_call_function(env, tsModule, testLoadModule, 1, &argv, &result); 
    return result;
}


static napi_value NAPI_Global_NativeCallArkTSByLoadSysModule(napi_env env, napi_callback_info info) {
    napi_value sysModule;
    napi_load_module(env, "@ohos.display", &sysModule);
    napi_value func = nullptr; 
    napi_get_named_property(env, sysModule, "getDefaultDisplaySync", &func); 
    napi_value funcResult; 
    napi_call_function(env, sysModule, func, 0, nullptr, &funcResult); 
    napi_value widthValue = nullptr; 
    napi_get_named_property(env, funcResult, "width", &widthValue);
    return widthValue;
}

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
    napi_property_descriptor desc[] = {
        {"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"nativeCallArkTS", nullptr, NAPI_Global_nativeCallArkTS, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"NativeCallArkTSByObj", nullptr, NAPI_Global_nativeCallArkTSByObj, nullptr, nullptr, nullptr, napi_default,
         nullptr},
        {"NativeCallArkTSByLoadModule", nullptr, NAPI_Global_NativeCallArkTSByLoadModule, nullptr, nullptr, nullptr,
         napi_default, nullptr},
        {"NativeCallArkTSByLoadSysModule", nullptr, NAPI_Global_NativeCallArkTSByLoadSysModule, nullptr, nullptr,
         nullptr, napi_default, nullptr},
        {"testPromise", nullptr, TestPromise, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"doJobWithAsyncTask", nullptr, doJobWithAsyncTask, nullptr, nullptr, nullptr, napi_default,
         nullptr}};
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "clibrary",
    .nm_priv = ((void*)0),
    .reserved = { 0 },
};

extern "C" __attribute__((constructor)) void RegisterClibraryModule(void)
{
    napi_module_register(&demoModule);
}